import json
import logging

import requests

from django.contrib.auth import login
from django.core.exceptions import ObjectDoesNotExist
from django.forms import model_to_dict
from django_filters.rest_framework import FilterSet
from rest_framework.decorators import api_view
from rest_framework.generics import ListCreateAPIView, ListAPIView, RetrieveUpdateAPIView, GenericAPIView
from rest_framework.response import Response
from rest_framework.status import *
from rest_framework_simplejwt.views import TokenObtainPairView
from weixin import WXAPPAPI
from weixin.oauth2 import OAuth2AuthExchangeError

from oa.serializers import *
from oa.utils import calculate_product_batch_cost

logger = logging.getLogger('django')


@api_view(["GET"])
def api_root(request):
    urls = {'method': request.method, 'admin': '/admin/', 'api': '/api/'}
    return Response(urls)


class AppFilter(FilterSet):

    @classmethod
    def filter_for_field(cls, field, field_name, lookup_expr='exact'):
        filter_class = super().filter_for_field(field, field_name, lookup_expr)
        if lookup_expr == 'exact':
            filter_class.extra['help_text'] = '{0} 等于'.format(field.verbose_name)
        elif lookup_expr == 'contains':
            filter_class.extra['help_text'] = '{0} 包含'.format(field.verbose_name)
        elif lookup_expr == 'gte':
            filter_class.extra['help_text'] = '{0} 大于等于'.format(field.verbose_name)
        elif lookup_expr == 'gt':
            filter_class.extra['help_text'] = '{0} 大于'.format(field.verbose_name)
        elif lookup_expr == 'lt':
            filter_class.extra['help_text'] = '{0} 小于'.format(field.verbose_name)
        elif lookup_expr == 'lte':
            filter_class.extra['help_text'] = '{0} 小于等于'.format(field.verbose_name)
        return filter_class


class AppTokenObtainPairView(TokenObtainPairView):
    """
    Token Obtain API
    """
    serializer_class = AppTokenObtainPairSerializer


class AppListCreateApi(ListCreateAPIView):

    def perform_create(self, serializer):
        if self.request.user:
            serializer.validated_data['created_by_id'] = getattr(self.request.user, 'id')
        serializer.save()


class LoginAppApi(GenericAPIView):
    authentication_classes = ()
    permission_classes = ()

    def get_queryset(self):
        return None


def create_or_update_user_info(openid, user_info):
    """
    创建或者更新用户信息
    :param openid: 微信 openid
    :param user_info: 微信用户信息
    :return: 返回用户对象
    """
    if openid:
        if user_info:
            user, created = WxUser.objects.update_or_create(openid=openid, defaults=user_info)
        else:
            user, created = WxUser.objects.get_or_create(openid=openid)
        return user
    return None


def create_or_update_user_info_gzh(openid_gzh, user_info):
    """
    创建或者更新用户信息
    :param openid_gzh: 微信 openid
    :param user_info: 微信用户信息
    :return: 返回用户对象
    """
    if openid_gzh:
        if user_info:
            user, created = WxUser.objects.update_or_create(openid_gzh=openid_gzh, defaults=user_info)
        else:
            user, created = WxUser.objects.get_or_create(openid_gzh=openid_gzh)
        return user
    return None


def get_access_token(code):
    """
    公众号微信授权，通过 code 获取 access_token
    :param code: 前端获取的 code
    :return: 返回 {"access_token":"","expires_in":7200,"refresh_token":"","openid":"","scope":"snsapi_userinfo"}
    """
    data = dict()
    if code:
        res = requests.get(
            url='https://api.weixin.qq.com/sns/oauth2/access_token?'
                'appid={0}&'
                'secret={1}&'
                'code={2}&'
                'grant_type=authorization_code'.format(
                    settings.WX_GZH_APP_ID,
                    settings.WX_GZH_APP_SECRET,
                    code))
        if res.status_code == 200:
            data = res.json()
    return data


def get_wx_gzh_user_info(code):
    """
    公众号微信授权，通过 code 获取用户信息
    :param code: 前端获取的 code
    :return: {
          "openid":" OPENID",
          "nickname": NICKNAME,
          "sex":"1",
          "province":"PROVINCE",
          "city":"CITY",
          "country":"COUNTRY",
          "headimgurl": "",
          "privilege":[ "PRIVILEGE1" "PRIVILEGE2"],
          "unionid": ""
        }
    """
    data = get_access_token(code)
    user_info = dict()
    access_token = data.get('access_token')
    openid = data.get('openid')
    if access_token and openid:
        res = requests.get(
            url='https://api.weixin.qq.com/sns/userinfo?access_token={0}&openid={1}&lang=zh_CN'.format(
                access_token,
                openid))
        if res.status_code == 200:
            user_info = res.json()
    return user_info


class WxGzhLoginView(LoginAppApi):
    """
    post:
    公众号登录接口，传递参数 {'code': ''}
    """
    authentication_classes = []
    permission_classes = ()
    fields = {
        'openid': 'openid',
        'nick_name': 'nickname',
        'language': 'language',
        'gender': 'sex',
        'city': 'city',
        'province': 'province',
        'country': 'country',
        'avatar_url': 'headimgurl',
    }
    serializer_class = WxSerializer

    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        data = serializer.validated_data
        user_info = dict()
        code = data.get('code')
        logger.info("Code: {0}".format(code))
        # 通过 code 获取用户信息
        user_info_raw = get_wx_gzh_user_info(code)
        if isinstance(user_info_raw, str):
            user_info_raw = json.loads(user_info_raw)
        if not isinstance(user_info_raw, dict):
            user_info_raw = {}
        logger.info("user_info: {0}".format(user_info_raw))
        if user_info_raw:
            openid_gzh = None
            for k, v in self.fields.items():
                if k == 'openid':
                    openid_gzh = user_info_raw.get('openid')
                else:
                    current_v = user_info_raw.get(v)
                    if current_v:
                        if isinstance(current_v, str):
                            current_v = current_v.encode('iso-8859-1').decode('utf-8')
                        user_info[k] = current_v
            if openid_gzh:
                user = create_or_update_user_info_gzh(openid_gzh, user_info)
                if user:
                    token = AppTokenObtainPairSerializer.get_token(user).access_token
                    return Response(
                        {
                            'jwt': str(token),
                            'user': model_to_dict(
                                user,
                                fields=[
                                    'nick_name', 'full_name', 'mobile', 'gender', 'avatar_url',
                                    'is_partner', 'is_client', 'is_manager'
                                ])
                        },
                        status=HTTP_200_OK)
        return Response({'jwt': None, 'user': {}}, status=HTTP_204_NO_CONTENT)


class LoginAccountView(LoginAppApi):
    serializer_class = LoginSerializer

    def post(self, request):
        res = {
            'data': {
                'id': 0,
                'rid': 0,
                'username': '',
                'email': '',
                'token': ''
            },
            'meta': {
                'msg': '',
                'status': 400
            }
        }
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        validated_data = serializer.validated_data
        username = validated_data.get('username')
        password = validated_data.get('password')
        if username and password:
            username = username.strip()
            try:
                user = WxUser.objects.get(username=username)
                if user.check_password(password):
                    token = AppTokenObtainPairSerializer.get_token(user).access_token
                    # 浏览器登录
                    login(request, user)
                    # 返回数据
                    res['data']['id'] = user.pk
                    res['data']['username'] = user.username
                    res['data']['email'] = user.email
                    res['data']['token'] = 'Bearer {}'.format(str(token))
                    res['meta']['msg'] = '登录成功'
                    res['meta']['status'] = 200
                else:
                    res['meta']['msg'] = '用户名或密码错误'
            except ObjectDoesNotExist:
                res['meta']['msg'] = '用户名或密码错误'
        else:
            res['meta']['msg'] = '用户名或密码填写不完整'
        return Response(res, status=HTTP_200_OK)


class LoginCaptchaView(LoginAppApi):
    serializer_class = LoginSerializer
    pagination_class = []

    def get(self, request):
        mobile = self.request.query_params.get('mobile')
        return Response({'res': mobile}, status=HTTP_200_OK)


class CurrentUserView(LoginAppApi):

    def get(self, request):
        res = {
            'avatar': 'string',
            'name': 'string',
            'title': 'string',
            'group': 'string',
            'signature': 'string',
            'tags': [{
                'key': 'string',
                'label': 'string',
            }],
            'userid': 'string',
            'unreadCount': 'number',
        }
        return Response(res, status=HTTP_200_OK)


class WxLoginView(LoginAppApi):
    """
    post:
    小程序的微信登录接口，传递参数 {'code': '', 'user_info': ''}
    """
    fields = {
        'nick_name': 'nickName',
        'gender': 'gender',
        'language': 'language',
        'city': 'city',
        'province': 'province',
        'country': 'country',
        'avatar_url': 'avatarUrl',
    }
    serializer_class = WxSerializer

    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        data = serializer.validated_data
        user_info = dict()
        code = data.get('code')
        logger.info("Code: {0}".format(code))
        user_info_raw = data.get('user_info', {})
        if isinstance(user_info_raw, str):
            user_info_raw = json.loads(user_info_raw)
        if not isinstance(user_info_raw, dict):
            user_info_raw = {}
        logger.info("user_info: {0}".format(user_info_raw))
        if code:
            api = WXAPPAPI(appid=settings.WX_APP_ID, app_secret=settings.WX_APP_SECRET)
            try:
                session_info = api.exchange_code_for_session_key(code=code)
            except OAuth2AuthExchangeError:
                session_info = None
            if session_info:
                openid = session_info.get('openid', None)
                if openid:
                    if user_info_raw:
                        for k, v in self.fields.items():
                            user_info[k] = user_info_raw.get(v)
                    user = create_or_update_user_info(openid, user_info)
                    if user:
                        token = AppTokenObtainPairSerializer.get_token(user).access_token
                        return Response(
                            {
                                'jwt': str(token),
                                'user': model_to_dict(
                                    user,
                                    fields=[
                                        'full_name', 'mobile', 'gender',
                                        'is_partner', 'is_client', 'is_manager'
                                    ])
                            },
                            status=HTTP_200_OK)
        return Response({'jwt': None, 'user': {}}, status=HTTP_204_NO_CONTENT)


class StaffFilter(AppFilter):
    class Meta:
        model = Staff
        fields = {
            'first_name': ['exact', 'contains'],
            'last_name': ['exact', 'contains'],
        }


class StaffListView(ListAPIView):
    queryset = Staff.objects.order_by('last_name')
    serializer_class = StaffSerializer
    filterset_class = StaffFilter


class StaffAppRetrieveUpdateAPIView(RetrieveUpdateAPIView):
    serializer_class = StaffSerializer
    queryset = Staff.objects.all()


class ProductFilter(AppFilter):
    class Meta:
        model = ProductName
        fields = {
            'code': ['exact', 'contains'],
            'name': ['exact', 'contains'],
            'name_en': ['exact', 'contains'],
            'product_category__name': ['exact', 'contains'],
        }


class ProductListView(ListAPIView):
    queryset = ProductName.objects.filter(is_active=True, is_oem=False, related_name__isnull=True).order_by('code')
    serializer_class = ProductSimpleSerializer
    filterset_class = ProductFilter
    search_fields = ['code', 'name', 'name_en', 'product_category__name']


class ProductBatchListView(ListAPIView):
    queryset = ProductBatch.objects.all()
    serializer_class = ProductBatchSerializer
    search_fields = ['code']


class ProductBatchCostView(GenericAPIView):

    def get_queryset(self):
        return None

    def get(self, request, pk):
        data = {'err': 'no access'}
        if self.request.user:
            if self.request.user.is_staff:
                data = calculate_product_batch_cost(pk, date_before=self.request.query_params.get('date_before'))
                if not data:
                    data = {'err': 'not found'}
        return Response(data, status=HTTP_200_OK)


class MaterialPublicListView(ListAPIView):
    permission_classes = ()
    queryset = Material.objects.filter(is_public=True).exclude(code__isnull=True)
    serializer_class = MaterialPublicSerializer
